using System;
using System.Diagnostics;

namespace LowLevelGraphics
{
    /// <summary>
    /// Complex number
    /// Class representing a complex number.
    /// </summary>
    [DebuggerDisplay("real: {real}, imaginary: {imaginary}")]
    public class ComplexNumber
    {
        /// The real part of the complex number.
        public double real;

        /// The imaginary part of the complex number.
        public double imaginary;

        /// <summary>
        /// Default no-arg constructor.
        /// </summary>
        public ComplexNumber()
        {
        }

        /// <summary>
        /// Constrctor taking two doubles, the real and the imaginary values.
        /// </summary>
        /// <param name="r">double representing the real value.</param>
        /// <param name="i">double representing the imaginary value.</param>
        public ComplexNumber(double _real, double _imaginary)
        {
            real = _real;
            imaginary = _imaginary;
        }

        /// <summary>
        /// Constructor taking a ComplexNumber to make an identical copy.
        /// </summary>
        /// <param name="c">ComplexNumber to be copied.</param>
        public ComplexNumber(ComplexNumber c)
        {
            real = c.real;
            imaginary = c.imaginary;
        }

        /// <summary>
        /// Method to obtain the magnitude of the complex number.
        /// </summary>
        /// <returns>double representing the magnitude.</returns>
        public double magnitude()
        {
            return Math.Sqrt(this.cNorm());
        }

        /// <summary>
        /// Method to obtain the phase angle (in radians) of the complex number.
        /// </summary>
        /// <returns>double representing the phase angle in radians.</returns>
        public double phaseAngle()
        {
            if (real == 0 && imaginary == 0) return 0;
            else return Math.Atan(imaginary / real);
        }

        double cNorm()
        {
            return real * real + imaginary * imaginary;
        }

        /// <summary>
        /// Method to obtain the exp of a complex number.
        /// </summary>
        /// <param name="z">ComplexNumber input.</param>
        /// <returns>ComplexNumber output.</returns>
        public static ComplexNumber cExp(ComplexNumber z)
        {
            ComplexNumber x, y;
            x = new ComplexNumber(Math.Exp(z.real), 0.0);
            y = new ComplexNumber(Math.Cos(z.imaginary), Math.Sin(z.imaginary));
            return cMult(x, y);
        }

        /// <summary>
        /// Method to multiply two complex numbers together.
        /// </summary>
        /// <param name="z1">z1 ComplexNumber.</param>
        /// <param name="z2">z2 ComlexNumber.</param>
        /// <returns>z3 ComplexNumber z3 = z1 * z2.</returns>
        public static ComplexNumber cMult(ComplexNumber z1, ComplexNumber z2)
        {
            ComplexNumber z3 = new ComplexNumber();
            z3.real = (z1.real) * (z2.real) - (z1.imaginary) * (z2.imaginary);
            z3.imaginary = (z1.real) * (z2.imaginary) + (z1.imaginary) * (z2.real);
            return z3;
        }

        /// <summary>
        /// Method to add two complex numbers together.
        /// </summary>
        /// <param name="z1">z1 ComplexNumber.</param>
        /// <param name="z2">z2 ComlexNumber.</param>
        /// <returns>z3 ComplexNumber z3 = z1 + z2.</returns>
        public static ComplexNumber cSum(ComplexNumber z1, ComplexNumber z2)
        {
            ComplexNumber z3 = new ComplexNumber();
            z3.real = z1.real + z2.real;
            z3.imaginary = z1.imaginary + z2.imaginary;
            return z3;
        }

        /// <summary>
        ///
        /// Method to divide a complex number by another.
        /// </summary>
        /// <param name="z1">z1 ComplexNumber.</param>
        /// <param name="z2">z2 ComlexNumber.</param>
        /// <returns>z3 ComplexNumber z3 = z1 / z2.</returns>
        public static ComplexNumber cDiv(ComplexNumber z1, ComplexNumber z2)
        {
            ComplexNumber z3 = new ComplexNumber();
            double n = z2.cNorm();

            z3.real = ((z1.real * z2.real) + (z1.imaginary * z2.imaginary)) / n;
            z3.imaginary = ((z2.real * z1.imaginary) - (z1.real * z2.imaginary)) / n;
            return z3;
        }

        /// <summary>
        /// Method to subtract a complex number from another.
        /// </summary>
        /// <param name="z1">z1 ComplexNumber.</param>
        /// <param name="z2">z2 ComlexNumber.</param>
        /// <returns>z3 ComplexNumber z3 = z1 - z2.</returns>
        public static ComplexNumber cDif(ComplexNumber z1, ComplexNumber z2)
        {
            ComplexNumber z3 = new ComplexNumber();

            z3.real = z1.real - z2.real;
            z3.imaginary = z1.imaginary - z2.imaginary;
            return z3;
        }
    }
}